home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / Snippets / Toolbox / 'vox ' recording / VoxII.c next >
Encoding:
C/C++ Source or Header  |  1992-07-15  |  8.9 KB  |  341 lines  |  [TEXT/MPS ]

  1. /*______________________________________________________*/
  2. /*                       Sound  Demo                        */
  3. /*                          by                          */
  4. /*                  RICHARD P. COLLYER                  */
  5. /*              Developer Technical Support             */
  6. /*                 Apple Computer, Inc.                 */
  7. /*                       09/24/91                       */
  8. /*______________________________________________________*/
  9.  
  10. #include    <GestaltEqu.h>
  11. #include    <Memory.h>
  12. #include    <OSEvents.h>
  13. #include    <Sound.h>
  14. #include    <SoundInput.h>
  15. #include    <StdIO.h>
  16. #include    <Types.h>
  17.  
  18. //The smaller buffSize, the sooner the echo, but the more clicks you hear
  19. #define kbuffSize        0x10000
  20.  
  21. /*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
  22. The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
  23. pointing at a SoundHeader Record and not an 'snd ' resource header. */
  24. #define kHeaderSize        20
  25.  
  26. // Forward reference
  27. pascal void MyRecComp (SPBPtr inParamPtr);
  28.  
  29. typedef struct {
  30.     short        OnOff;
  31.     short         Level;
  32.     short         Length;
  33. } Level;
  34.  
  35. void BufPlay (Handle Buf, SndChannelPtr    channel)
  36.  
  37. /*This routine takes an snd buffer and a sound channel and turns the information into a 
  38. bufferCmd to the channel. */
  39.  
  40. {
  41.     SndCommand        localSndCmd;
  42.     OSErr            err;
  43.     
  44.     localSndCmd.cmd = bufferCmd;
  45.     localSndCmd.param1 = 0;
  46.     localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
  47.     
  48.     err = SndDoCommand (channel, &localSndCmd, false);
  49.     if (err != noErr)
  50.         Debugger();
  51.     return;
  52. }
  53.  
  54. Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
  55.  
  56. /* SetUpSounds is a routine which takes a buffer handle, sound headers size and sample rate 
  57. value and turns it into a snd buffer with the proper header.  It then returns the buffer
  58. handle back to the caller. */
  59.  
  60. {
  61.     OSErr        err;
  62.     short        dummy;
  63.     
  64.     Buf = NewHandle(kbuffSize);
  65.     if (MemError() != noErr || Buf == nil)
  66.         Debugger();
  67.     MoveHHi (Buf);
  68.     if (MemError() != noErr)
  69.         Debugger();
  70.     HLock (Buf);
  71.     if (MemError() != noErr)
  72.         Debugger();
  73.     
  74.     /* The call to SetupSndHeader is done twice in order to make sure that the 
  75.     Header size is set correctly.  */
  76.  
  77.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
  78.     if (err != noErr)
  79.         Debugger();
  80.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, kbuffSize - *HeaderSize, &dummy);
  81.     if (err != noErr)
  82.         Debugger();
  83.         
  84.     return (Buf);
  85. }
  86.  
  87. main()
  88. {
  89.     short                dummy;
  90.     Level                myLevel;
  91.     OSErr                err;
  92.     SPBPtr                RecordRec;
  93.     long                SoundRefNum, feature;
  94.     Handle                Buffer, Buffer2, Buffer3, Buffer4;
  95.     SndChannelPtr        chan;
  96.     short                headerlength, headerlength2, headerlength3, headerlength4;
  97.     Fixed                sampleRate;
  98.     SndCommand            mycmd;
  99.     short                recordStat, MetLevel;
  100.     unsigned long        totalSampleRecord, TotalMilRecord, ActSampleRecord, ActMilRecord;
  101.     
  102.     /* use Gestalt to make sure the app will work */
  103.     
  104.     err = Gestalt(gestaltSoundAttr, &feature);
  105.     if (!err) {
  106.         if ((feature & 0x0020) != 0x0020)
  107.             ExitToShell();    // a nice app would inform the user before quiting
  108.         }
  109.     else 
  110.         Debugger();
  111.         
  112.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  113.     
  114.     err = SPBOpenDevice (nil, siWritePermission, &SoundRefNum);
  115.     if (err != noErr)
  116.         Debugger();
  117.         
  118.     myLevel.OnOff = 1;
  119.     myLevel.Level = 150;
  120.     myLevel.Length = 0;
  121.     err = SPBSetDeviceInfo (SoundRefNum,siVoxRecordInfo, (Ptr) &myLevel);
  122.     if (err != noErr)
  123.         Debugger();
  124.         
  125.     myLevel.OnOff = 1;
  126.     myLevel.Level = 50;
  127.     myLevel.Length = 100;
  128.     err = SPBSetDeviceInfo (SoundRefNum,siVoxStopInfo, (Ptr) &myLevel);
  129.     if (err != noErr)
  130.         Debugger();
  131.         
  132.     /* Get the sample rate information for the snd header */
  133.         
  134.     err = SPBGetDeviceInfo (SoundRefNum,siSampleRate, (Ptr) &sampleRate);
  135.     if (err != noErr)
  136.         Debugger();
  137.         
  138.     /* build the four snd buffers */
  139.         
  140.     Buffer = SetUpSounds (Buffer, &headerlength, sampleRate);
  141.     Buffer2 = SetUpSounds (Buffer2, &headerlength2, sampleRate);
  142.     Buffer3 = SetUpSounds (Buffer3, &headerlength3, sampleRate);
  143.     Buffer4 = SetUpSounds (Buffer4, &headerlength4, sampleRate);
  144.     
  145.     /* build the RecordRec pointer and fill in the fields */
  146.         
  147.     RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
  148.     if (MemError() != noErr || RecordRec == nil)
  149.         Debugger();
  150.  
  151.     RecordRec->inRefNum = SoundRefNum;
  152.     RecordRec->count =  kbuffSize - headerlength;
  153.     RecordRec->milliseconds = 0;
  154.     RecordRec->bufferLength = kbuffSize - headerlength;
  155.     RecordRec->bufferPtr = (Ptr) ((*Buffer) + headerlength);
  156.     RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
  157.     RecordRec->interruptRoutine = nil;
  158.     RecordRec->userLong = 0;
  159.     RecordRec->error = 0;
  160.     RecordRec->unused1 = 0;
  161.     
  162.     /* open the sound channel which I will need to play from */
  163.  
  164.     chan = nil;
  165.     err = SndNewChannel (&chan, sampledSynth, 0, nil);
  166.     if (err != noErr)
  167.         Debugger();
  168.             
  169.     do {    /* main loop of the app */
  170.         err = SPBRecord (RecordRec, true); // start recording
  171.         if (err != noErr)
  172.             Debugger();
  173.             
  174.         /* The set of if-then-else statements are to determine if the first three buffers
  175.         have been filled with recorded sounds.  If so then which buffer was the last buffer
  176.         to be filled and set the appropriate buffer to be the next buffer to be played. */
  177.         
  178.         if ((RecordRec->userLong & 0x00000008) == 0x00000008) {
  179.             if (RecordRec->bufferPtr == (Ptr) ((*Buffer) + headerlength)) {
  180.                 err = SetupSndHeader (Buffer3, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
  181.                 if (err != noErr)
  182.                     Debugger();
  183.  
  184.                 BufPlay (Buffer3, chan);
  185.                 }
  186.             else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer2) + headerlength2)) {
  187.                 err = SetupSndHeader (Buffer4, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
  188.                 if (err != noErr)
  189.                     Debugger();
  190.  
  191.                 BufPlay (Buffer4, chan);
  192.                 }
  193.             else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer3) + headerlength3)) {
  194.                 err = SetupSndHeader (Buffer, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
  195.                 if (err != noErr)
  196.                     Debugger();
  197.  
  198.                 BufPlay (Buffer, chan);
  199.                 }
  200.             else {
  201.                 err = SetupSndHeader (Buffer2, 1, sampleRate, 8, 'NONE', 0x3C, ActSampleRecord, &dummy);
  202.                 if (err != noErr)
  203.                     Debugger();
  204.  
  205.                 BufPlay (Buffer2, chan);
  206.                 }
  207.             }
  208.             
  209.         do {    /* loop until the recording is done on current buffer */
  210.             } while ((RecordRec->userLong & 0x00000001) == 0);
  211.             
  212.         // Use this for testing the resulting recorded sound, but only when testing
  213.             
  214.         err = SPBGetRecordingStatus(SoundRefNum, &recordStat, &MetLevel, &totalSampleRecord,
  215.                 &ActSampleRecord, &TotalMilRecord, &ActMilRecord);
  216.         if (err != noErr)
  217.             Debugger();
  218.             
  219.         /* Once the Completion rountine has been called I need to set up the next buffer
  220.         to be recorded into.  I do this by going through the if-then-else cases to see 
  221.         which buffer was last filled and set up the next buffer. */
  222.         
  223.         if (RecordRec->bufferPtr == (Ptr) ((*Buffer) + headerlength))
  224.             RecordRec->bufferPtr = (Ptr) ((*Buffer2) + headerlength2);
  225.             
  226.         else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer2) + headerlength2))
  227.             RecordRec->bufferPtr = (Ptr) ((*Buffer3) + headerlength3);
  228.             
  229.         else  if (RecordRec->bufferPtr == (Ptr) ((*Buffer3) + headerlength3))
  230.             RecordRec->bufferPtr = (Ptr) ((*Buffer4) + headerlength4);
  231.             
  232.         else
  233.             RecordRec->bufferPtr = (Ptr) ((*Buffer) + headerlength);    
  234.         
  235.         /* I need to reset the recording current buffer field*/
  236.         
  237.         RecordRec->userLong &= 0xFFFFFFFE;
  238.         
  239.         /* When the mouse button is found down, it is time to quit */
  240.         
  241.         } while (!Button());
  242.         
  243.     /* Once I am out of the loop it is time to clean up - stop the currently playing sound,
  244.     Dispose of the Channel, close the input driver, and dispose of the buffer handles and
  245.     RecordRec Ptr. */
  246.         
  247.     mycmd.cmd = quietCmd;
  248.     mycmd.param1 = 0;
  249.     mycmd.param2 = 0;
  250.     
  251.     err = SndDoImmediate (chan, &mycmd);
  252.     if (err != noErr)
  253.         Debugger();
  254.  
  255.     err = SndDisposeChannel (chan,false);
  256.     if (err != noErr)
  257.         Debugger();
  258.  
  259.     SPBCloseDevice (SoundRefNum);
  260.         
  261.     HUnlock (Buffer);
  262.     HUnlock (Buffer2);
  263.     HUnlock (Buffer3);
  264.     HUnlock (Buffer4);
  265.     DisposeHandle (Buffer);
  266.     DisposeHandle (Buffer2);
  267.     DisposeHandle (Buffer3);
  268.     DisposeHandle (Buffer4);
  269.     DisposePtr ((Ptr) RecordRec);
  270. }
  271. /**********************************/
  272.  
  273. pascal void MyRecComp (SPBPtr inParamPtr)
  274.  
  275. /* This is the Completion Routine which is called every time the buffer, which is being 
  276. recorded into, is full.  */
  277.  
  278. {
  279.     long    local;
  280.     
  281.     local = inParamPtr->userLong;
  282.     
  283.     local &= 0x00000038;
  284.     
  285.     /* I need to check for the first three times this routine is called so I know when it
  286.     is safe to start playing buffers. */
  287.     
  288.     if (local != 0x00000008) { 
  289.         if (local == 0x00000000)
  290.             local = 0x00000010;
  291.         else if (local == 0x00000010)
  292.             local = 0x00000020;
  293.         else
  294.             local = 0x00000008;
  295.         }
  296.         
  297.     inParamPtr->userLong &= 0xFFFFFFC5;    
  298.     inParamPtr->userLong |= local;    
  299.     
  300.     /* inform the app that the recording is done */
  301.         
  302.     inParamPtr->userLong |= 0x00000001;    
  303.  
  304.     return;
  305. }
  306.  
  307. /**********************************/
  308. /* Bit definitions .....
  309. 31
  310. 30
  311. 29
  312. 28
  313. 27
  314. 26
  315. 25
  316. 24
  317. 23
  318. 22
  319. 21
  320. 20
  321. 19
  322. 18
  323. 17
  324. 16
  325. 15
  326. 14
  327. 13
  328. 12
  329. 11
  330. 10
  331. 09
  332. 08
  333. 07
  334. 06
  335. 05 - finished recording to the second buffer
  336. 04 - finished recording to the first buffer
  337. 03 - finished recording to the first three buffers
  338. 02
  339. 01
  340. 00 - finished recording to current buffer
  341. */